Skip navigation and go to content

Fix Focus and Operability Issues for Keyboard Interactivity

On this page

🛠 Challenge: Update Focus Styling and Markup for Keyboard Interactivity

We’ve identified a few big things that need to be fixed and all we had to do was hit the Tab key!

Your challenge is to remediate the issues we found.

Issue 1: Update the stylesheet so that focus styles are visible. The styles can be found at scss/_defaults.scss.

💡Tip

The CampSpots app uses Sass, but all of the CSS you know and love will still work.

Issue 2: The MegaNav needs its items to be replaced with a semantic element that is focusable and operable. The markup can be found at _includes/header.html

💡Tip

Parcel is the build tool used for this version of the CampSpots app. It has been configured to use the posthtml-include package that allows for HTML from separate files to be referenced during development and combined at build time.

Issue 3: The links inside of the MegaNav sections should not be focusable when the menu is closed.

🛠 Solution Part 1: Fix the Outline Styles

Inside of scss/_defaults.scss at the top of the file is where the wildcard selector rule is that applies styles to every element.

Removing the outline: 0; line will allow for focused elements to show an outline:

Before:

* {
    box-sizing: border-box;
    outline: 0;
}

After:

* {
    box-sizing: border-box;
}
Video: Fix CSS Outline
Loaded: 2%
Current Time 0:00
/
Duration Time 4:20
Video Transcript

So I think, let, I think I'm, I'm ready to make some changes. How about you let's make some improvements to this mega menu. So I'll show you where this code lives. Let's go back over to visual studio code. And so the first page we'll look at is our homepage index HTML.

It is in the root of this whole project folder, and it's just plain old HTML and CSS. We are using SAS. So this S CSS we're using the parcel bundler. That's, what's doing the, the conversion from our styles dot SCSS into regular CSS that the browser can can use. It's also using this include. So we've got an include for the header that makes it nice, so that when we're working on something, that's a shared component across pages, even though we're in kind of traditional HTML, we don't have to go make changes to the header on every single page.

Cause that would be a pain. So the header include is up here in the includes directory and then header dot HTML. This is where the code for the mega menu lives. So we've got that. We can make changes too. I'll also show you the SCSS directory down here. Let's see, to figure out where our outline non code lives, cuz I know where it is, but I wanna show you how to find it.

So coming back up here to our a HF for the logo, if I were trying to figure out where in the heck these styles live. So I'm sort of from the outside, looking in, there is a link here in the dev tools that if I click on it, it takes me to this compiled CSS and it's all, you know, obfuscated and or it's like made into one line for performance reasons.

So I'm gonna format it using these little curly braces here in the dev tools that will make it go all the way down in one column and be much easier to read. So this shows me well right here at the beginning in this case it is not too deep into our CSS. I can see this outline none. It's still kind of commented out from where I toggled it in the dev tools.

So it's next to our variables kind of in our generic code. Sometimes this is a way that I can narrow down where the CSS lives. I think more likely that's gonna help you find stuff is in visual studio code. We can actually search. That might be helpful. So if I look for outline zero, I see it exists in defaults.

There's another one in passes. We'll leave that alone for now, but here's where. That code lived. So the compiled one and the browser, like, okay, we saw it where it kind of ended up compiled that didn't do a super effective job of showing us where it might live in our source code. Cuz we have a whole system that's bundling everything together.

So the search and vs code, I think that's how I often find stuff. You know, I'll look for a CSS selector or you know, a little bit of CSS, something that's recognizable, so I can go figure out where that lives. Cuz there's diagnosing the problem in the browser, but then trying to fix it. Sometimes it gimme a challenge to figure out where exactly to fix it.

In this case, on our wildcard selector, let's get that outline done out of there. I'm just gonna hit delete command save. And now if we come back over here to the browser and hit refresh when I tab through here now, anything that is that is reachable , including stuff that shouldn't be reachable has a visible focus style. And I have to tell you how often I see this outline zero and the reasons vary why that happens, why outline none makes it into style sheets.

I think often it's a misconception about what focuses for and teams that are just really used to the mouse. And they only expect users to use the mouse. They don't know what they're taking away by removing that I've heard the blue outline called ugly. You know, not part of our brand. There's so many reasons that are all misguided.

At least after replace it with something else. You can do custom focus styles, however, using the browser default, always a good way to go. It's easier. So by taking that outline zero off of there, now we can reach these items.

So let's come back over here to visual studio code, we made one major change that's super user impacting.

🛠 Solution Part 2: Update the MegaNav Items

Inside of the Header at _includes/header.html each of the three divs for the top-level MegaNav items needs to be changed to be a semantic button.

💡Tip

The semantic button element is focusable, keyboard operable, and gives us other accessibility wins for free. Learn more about this in the Semantics & ARIA workshop.

Before the first item:

<div class="megamenu-navitem header-main-item" id="megamenu-item1">
	Plan Your Trip
</div>

After:

<button class="megamenu-navitem header-main-item" id="megamenu-item1">
	Plan Your Trip
</button>

Change the other two megamenu-navitem divs to use the button element as well.

Now the menu items are focusable, but the styling is off.

The MegaNav buttons don’t match the original design.Loading

Video: Change MegaNav divs to buttons
Loaded: 2%
Current Time 0:00
/
Duration Time 3:48
Video Transcript

So let's find some more, shall we so defaults, we made that change. I'm gonna close the terminal. So we also had some other changes to make in our headers. So within the SCSS directory, I'm gonna come down here to header dot SCSS. . And so there's a whole lot of code here for our header that the way these CSS classes were written depends on a lot of dev elements.

And so if you're writing a, a mega menu from scratch with more semantic elements, like we will do later this week, you might style it differently. Because you have more options when you have other elements than div . So, don't judge too hard. over our CSS. Cuz it's, it's mimicking menus and elements that we see out in the wild, including if you're Googling how to make a mega menu and you come across something like w three schools, you will find a lot of div stuff.

A lot of inaccessible examples. And so we wanna learn how to anticipate those how to know how to work around them and how to make better examples ourselves. So I'm scrolling down here, I'm looking for Heer header, mega menu, some mega menu nav item. And what I can do is do a little comparison side by side.

I'm gonna do command B to close the sidebar and I am going to control click header dot CSS and DS code and split. Right? And then over here on my left side of vs code, I have header dot HTML open. So I've got the CSS and the HTML side by side. I could also compare against the browser, but I'd be flipping back and forth between vs code and the browser.

So when I'm making changes like this, and I know I've got CSS for presentation and markup for the structure and content, if I've got them side by side, that can help as I make changes. So these items plan your trip ways to stay. These are our top level items and I'm collapsing these sub menus. So I can see more at a glance.

So we've got resources, ways to stay and plan your trip. And they've got a couple of CSS classes on them, mega menu, nav item, and header main item might be a alluding to a change we'll make later. So the first problem we saw was that these are divs, right? So they're not reachable with the keyboard. We can't tab onto them and do anything with them.

So we should change these to button elements. So I'm gonna select the div part of this tag, and I'm gonna do command D to select its closing div. So there's the opening div closing, div and no other divs in between. So I could select multiple at once with command D on my Mac. And that way I can type the word button.

and it has affected the opening and closing tag of this element. I'm gonna come down here to the other two and do the same, we'll change these from buttons to divs. And then one more resources. I believe that is it. Yeah. So we have three of these top level items that we' not reachable before. I'm gonna hit save in our header HTML file and kind of looking over here on the right side, in our CSS, we do have this matching CSS class for mega menu nav item, and it has things like color.

It's got a cursor pointer style because that dev element didn't look interactive to mouse users. So what do you do? You put a cursor pointer on it, make it look interactive, but it was only interactive to mouse users. It also has font size padding and a position of relative. And so when we go and make changes, we've changed it from a div to a button element and that might affect the style, but let's go see it in the browser and see if there's anything that we need to kind of override.

So I'm gonna do command tab to switch applications. And if I hit refresh, aha, I see some changes.

Update MegaNav Button Styling

Any time you change from a div to a button, there are going to be some styling tweaks that need to be made. (This is not an excuse to avoid using button elements!)

This CSS can be added to scss/_header.scss to make the CampSpots MegaNav buttons look good:

/* inside scss/_header.scss */

button.megamenu-navitem {
    background-color: transparent;
    border: none;
    color: inherit;
    cursor: pointer;
    display: inline-block;
    font-size: 0.9rem;
    margin: 0;
    padding: 1.9em;
    position: relative;
}

Video: Update MegaNav Button Styling
Loaded: 4%
Current Time 0:00
/
Duration Time 2:16
Video Transcript

So now these items are still sitting in the right places, but they've got borders little bit of rounded corners from border radius. The background color changed from transparent to a gray. That's kind of the, the default for button elements.

So we need to make some change there, but now I can actually reach these items. So that's awesome. I am still going through the mega menu sub menus, but we've got an improvement in that the top level items are reachable now. Awesome. So let's go make some changes going back to visual studio code with command tab and under mega menu nav item, let's add some CSS to make these look like they did before, because frequently what I see happen is teams will choose an element for the style, not for its functionality, which is unfortunate because CSS, we can style things any way we want almost.

I mean, there's definitely some, some cases where we have restrictions like form controls and things, but with buttons we can make 'em look however we want, and then they'll be focusable and interactive from the keyboard. So let's turn the border off with border colon, none. I think there was border radius too.

We could zero that out. I am very particular about my CSS, so I alphabetize , I'm gonna say background color, transparent, get rid of that gray and I'll hit command save on my Mac. So now I've got some reset styles. For this button, if I wanted to be more clear that these are buttons, I could also update the CSS selector to say button dot mega menu, nav item.

And that helps me a little bit when I'm scanning through the CSS to see, oh, these are buttons. It's really subjective whether you wanna make that change or not. But I think it does help to add more semantic details in there. And then if someone made a change, like if they changed it from a button back to a div, for some reason, CSS would break and they would have, they would need to rethink that.

that choice. Someone hit command S and I will do command tab to go back to the browser and hit command R for refresh. Cool. So now we've got these items that are reachable they're focusable, and we've zeroed out any kind of unintended styles that came when we switched from a dev element to a button element.

So there's no excuse now not to use a button element.

Prevent Focus on Hidden Items

The invisible links that can be reached by tabbing are inside the submenu.

The issue can be found inside the .megamenu-submenu rules:

/* excerpt from scss/_header.scss */
.megamenu-submenu {
    ...
		display: flex;
		...
}

The display: flex; declaration is rendering the menu to the DOM, even though it won’t be visible until it is hovered over with the mouse with the opacity rule:

.megamenu-section:hover .megamenu-submenu {
    height: 225px;
    opacity: 1;
    overflow: hidden;
    z-index: 1;
}

The first thing to do in order to prevent hidden items from being reachable is to change the submenu styles to use display: none; by default. This change will hide the content from keyboards and screen readers.

💡Tip

There’s a lot more on focus and CSS hiding techniques in the Interactions & Mechanics workshop!

Then we can change the :hover pseudo-classes on meganav-section rules to a new class we’ll call active. Note: while it is possible to create some accessible interactivity with HTML and CSS alone, a sprinkling of JavaScript and ARIA can create more robust patterns for Assistive Technologies.

Adding the display: flex; declaration will make the the menu show when the active class is also present:

.megamenu-section.active .megamenu-submenu {
		display: flex;
    height: 225px;
    opacity: 1;
    overflow: hidden;
    z-index: 1;
}

We can check our work in DevTools by manually adding the active class to the megamenu-section.

The active class on megamenu-section allows the submenu to be displayed.Loading
Video: Hide Submenus with CSS
Loaded: 3%
Current Time 0:00
/
Duration Time 2:24
Video Transcript

So to kind of finalize the CSS portion of this let's hide those sub menus. So we made a little toggle change in the dev tools. So we know if we change the display property of these sub menus, that we should be able to make these items not reachable.

Let's go change that. So I'm gonna go back to the BR or back to vs code command tab. So the CSS class for those is mega menu sub menu. And we can find that over here in our CSS, these are the styles that we fiddled with in the browser. So the default is display flex. And if I change that to display, none that will hide the contents from everyone it'll make it.

So you can't focus in there, screen readers, won't access that content. It's like a, it's a big hammer. We're just using that hammer to hide this sub menu from everyone, cuz everyone should be able to skip by stuff if it's not open. However, , however, that kind of introduces a problem in that our functionality works on a CSS hover.

So when the mouse user hovers over this, they're the only ones that are able to control this menu. And if we, I mean, I could change this hover to display flex and we'll do that just to restore the showing and hiding, but the hover is not enough for keyboard users. So let's go see how, what effect that had.

I'm gonna go command tab back to the browser command R to refresh. And so now Woohoo, I can skip past those mega menus, but if I try to use the keyboard like the inter key or the space bar on these top level buttons. I get nothing, nothing happens. I can still hover with my mouse, but we've only gone part of the way there to actually fix this mega menu for everyone.

So we're gonna need to employ some JavaScript. This is where our little bit of code is going to come in. So we're gonna write some JavaScript that will help us target these items and make it so that instead of hovering over them, when you click with a button or when you tab with the keyboard, we are going to apply a CSS class that will turn on and off.

So it'll be very similar in our CSS where we've got a hover instead of the hover, or maybe in addition to, we're gonna add some CSS that looks for an active CSS class, and we'll use JavaScript to toggle that on and off.

Add Additional Submenu Styling

Design-wise it would be good to retain the background color we saw when a menu item was hovered, so we'll add a declaration for that rule:

.megamenu-section.active .megamenu-navitem,
.megamenu-section:hover .megamenu-navitem {
    background-color: #f2f2f3;
}

Also as a styling tweak, there is also a carat that appears for the active submenu:

A carat displays for the active menu itemLoading

Updating the other :hover pseudo-class to use the active class will keep this functionality.

Before:

.megamenu-section:hover .megamenu-navitem:after {
		border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-bottom: 4px solid black;
		...

After:

.megamenu-section.active .megamenu-navitem:after {
		/* styles stay the same */
Video: More Menu CSS Updates
Loaded: 1%
Current Time 0:00
/
Duration Time 6:02
Video Transcript

And as much as we would like to be able to use CSS only for a lot of interactive things, we just can't for a lot of accessibility purposes. We need JavaScript to toggle accessibility states and to make things respond to user events. So JavaScript is not a bad word. It can be a bit of a learning curve.

I was there once too, but let's do a gentle scripting introduction to writing some JavaScript for a megamenu. Okay, so I'm gonna come back over to our code with Command + Tab and we are going to... I'm gonna, let's see. I'll probably keep our... Well, actually before we write the CSS, I have a trick.

So kind of doing it incrementally coming through to make changes, sometimes before you write the JavaScript you can kind of set up the markup and the CSS to be kind of the end state of how you want this stuff to work. So on each megamenu-section, so this wraps both the button, the top level button, and its submenu contents. So all of these sub links of the listings and all of those megamenu items that we were trying to hide from everyone, those are contained kind of further up the HTML tree in a single div.

So that would be nice. Like, if we were gonna put an active class, it would be nice if it contained both that button and the submenu, 'cause then we can style the button and the submenu when they're active, and all the functionality can kind of hinge off of that one parent element. So if I add that class, just like hard coded in there and hit save, then I can style off of it in its kind of final state and then we'll go write the JavaScript for it.

So any place in the CSS over here on the right where I see hover, that's a good candidate to go and add some code for this active class. So I copied this selector for megamenu-submenu within the megamenu-section when I'm hovering on it. So the full selector is megamenu-section:hover, for that hover sudo state, and then megamenu-submenu. I copy that. So I'm gonna drop down a line and paste it to add another declaration.

But instead of hover, 'cause it is the megamenu-section, this is the one that will have the active class. So instead of :hover, I'm gonna say .active to target a CSS class. So this way this will match when we're hovering and when the CSS class is present. Similarly, we've got a couple other mentions, like when we hover over a megamenu-navitem, there's a background color change. There's also within hovering, there's like a little triangle that shows up.

So you can see this little, teeny, just little carrot that kind of is a style to point to connect a megamenu to its top level item. So if the menu is active, it would be kind of nice if that was showing. So let's go change that. I'm gonna make that persist. So I'm copying a line, just to copy paste to make it go quickly. So this way we have multiple declarations separated by commas. So we have one for active, one for hover. And with our hard coded CSS class here in the markup for the first menu, that should be enough for us to see whether that works. It's not interactive at this point. This is the final state of when we add the interactivity with JavaScript.

It's kind of a nice way to just test your CSS styling. So I guess one choice we could make is do we want the top level item when we hover over it to show this background color? I think I might leave that as a... Maybe we could make it on focus or on hover. So it's kind of an interactive state for this top level item, but when the menu's open, maybe we don't make that persist.

I like that. Let's go back to VS Code. So that piece was right here, this hover. So I'm gonna copy this line and paste it on the line above, separated by a comma. And instead of hover, I'll do :focus. So we have a focus state and we have a hover state, and that way we've got some interactivity for that top level item, for the megamenu-navitem when it's focused. I guess that's when any item inside of the whole section is focused. So that might not actually match. We need the megamenu-nav item as the focusable.

So that's the button, the button item. See now I'm glad I added that button selector 'cause I can remember, what was that again? It's the button. So megamenu-navitem is actually the thing that we would be focused on. So I could make that clear here too. And that's because mega menu section is div. It's not actually a focusable thing. We do have, I guess as an alternative, sort of just playing here instead of putting it on the button, we could do what's called focus-in or focus-within.

So that is an element that sort of cascades from, if I'm focused on any element inside of the megamenu-section even though it itself is not focusable, and I'll show you what effect that has when we go back to the browser. So I'm gonna hit save. We'll double check our work make sure I didn't just add an unintentional anything.

Okay, so right now hit save. We've got that hard coded active state. If I focus through here, now when I'm focused anywhere inside of the megamenu, that top level item is showing that background color. So that's the focus-within, which is pretty cool. So if I Tab out of there, so now I'm focused on the next top level item even though our megamenu is kind of forced open with the CSS class, the focus within isn't matching anymore.

So now I'm on the next item and it's showing a background color. So that's pretty nifty. I think that's a good one to know about, because when you're focused within something you can sort of style around the HTML element tree, so to speak, so it's like stemming off of a parent element. We've got some really cool CSS for focus stuff now. So focus within that's neat.

In order to toggle the menu open and closed, we’ll use JavaScript to apply and remove the active class. We’ll look at that in the next lesson.